/****************************************************************************/
/*                                                                          */
/*   TRAFFIC2.C:  Traffic Light Controller using RTX-51                     */
/*                                                                          */
/*   17-NOV-1994 / EG                                                       */
/****************************************************************************/
/*   Derived from TRAFFIC.C (originally written for RTX tiny).              */
/*   Shows advanced features of the full version of RTX-51.                 */
/****************************************************************************/

#pragma CODE DEBUG OBJECTEXTEND

code char menu[] = 
   "\n"
   "+******* TRAFFIC LIGHT CONTROLLER using C51 and RTX-51 ********+\n"
   "| This program is a simple Traffic Light Controller.  Between  |\n"
   "| start time and end time the system controls a traffic light  |\n"
   "| with pedestrian self-service and approaching car detection.  |\n"
   "| Outside of this time range the yellow caution lamp is blink- |\n"
   "| ing.                                                         |\n"
   "+ command -+ syntax -----+ function ---------------------------+\n"
   "| Display  | D           | display times                       |\n"
   "| Time     | T hh:mm:ss  | set clock time                      |\n"
   "| Start    | S hh:mm:ss  | set start time                      |\n"
   "| End      | E hh:mm:ss  | set end time                        |\n"
   "+----------+-------------+-------------------------------------+\n";


#include <reg517.h>                   /* special function registers 80517   */
#include <rtx51.h>                    /* RTX-51 functions & defines         */
#include <stdio.h>                    /* standard I/O .h-file               */
#include <ctype.h>                    /* character functions                */
#include <string.h>                   /* string and memory functions        */


extern getline (char idata *, char);  /* external function: input line      */
extern serial_init ();                /* external function: init serial UART*/

#define INIT      0                   /* task number of task:  init         */
#define COMMAND   1                   /* task number of task:  command      */
#define CLOCK     2                   /* task number of task:  clock        */
#define BLINKING  3                   /* task number of task:  blinking     */
#define LIGHTS    4                   /* task number of task:  signal       */
#define KEYREAD   5                   /* task number of task:  keyread      */
#define GET_ESC   6                   /* task number of task:  get_escape   */
#define CAR_DET1  7                   /* task number of task:  car_det1     */
#define CAR_DET2  8                   /* task number of task:  car_det2     */

struct time  {                        /* structure of the time record       */
  unsigned char hour;                 /* hour                               */
  unsigned char min;                  /* minute                             */
  unsigned char sec;                  /* second                             */
};

struct time ctime = { 12,  0,  0 };   /* storage for clock time values      */
struct time start = {  7, 30,  0 };   /* storage for start time values      */
struct time end   = { 18, 30,  0 };   /* storage for end   time values      */

unsigned char keypressed1;            /* status flag: pedestrian button 1   */
unsigned char keypressed2;            /* status flag: pedestrian button 2   */
unsigned char cardetected1;           /* status flag: car detector 1        */
unsigned char cardetected2;           /* status flag: car detector 2        */

unsigned char phaseno;                /* Traffic control phase number       */

                                      /* Direction 1                        */
sbit  red___1 = P1^2;                 /* I/O Pin:  red    lamp output       */
sbit  yellow1 = P1^1;                 /* I/O Pin:  yellow lamp output       */
sbit  green_1 = P1^0;                 /* I/O Pin:  green  lamp output       */
sbit  stop__1 = P1^3;                 /* I/O Pin:  stop   lamp output       */
sbit  walk__1 = P1^4;                 /* I/O Pin:  walk   lamp output       */
sbit  key1    = P1^5;                 /* I/O Pin:  self-service key input   */
                                                                           
                                      /* Direction 2                        */
sbit  red___2 = P4^2;                 /* I/O Pin:  red    lamp output       */
sbit  yellow2 = P4^1;                 /* I/O Pin:  yellow lamp output       */
sbit  green_2 = P4^0;                 /* I/O Pin:  green  lamp output       */
sbit  stop__2 = P4^3;                 /* I/O Pin:  stop   lamp output       */
sbit  walk__2 = P4^4;                 /* I/O Pin:  walk   lamp output       */
sbit  key2    = P4^5;                 /* I/O Pin:  self-service key input   */

idata    char inline[16];             /* storage for command input line     */


/****************************************************************************/
/*        Task 0 'init': Initialize                                         */
/****************************************************************************/
void init (void) _task_ INIT  {       /* program execution starts here      */
  serial_init ();                     /* initialize the serial interface    */
  os_set_slice (10000);               /* set the system timebase to 10ms    */
  os_create_task (CLOCK);             /* start clock task                   */
  os_create_task (COMMAND);           /* start command task                 */
  os_create_task (LIGHTS);            /* start lights task                  */
  os_create_task (KEYREAD);           /* start keyread task                 */
  os_create_task (CAR_DET1);          /* start cardet1 task                 */
  os_create_task (CAR_DET2);          /* start cardet2 task                 */
  os_delete_task (INIT);              /* stop init task (no longer needed)  */
}


bit display_time = 0;                 /* flag: cmd state display_time       */

/****************************************************************************/
/*        Task 2 'clock'                                                    */
/****************************************************************************/
void clock (void)  _task_ CLOCK _priority_ 1 {
  while (1)  {                        /* clock is an endless loop           */
    if (++ctime.sec == 60)  {         /* calculate the second               */
      ctime.sec = 0;
      if (++ctime.min == 60)  {       /* calculate the minute               */
        ctime.min = 0;
        if (++ctime.hour == 24)  {    /* calculate the hour                 */
          ctime.hour = 0;
        }
      }
    }
    if (display_time)  {              /* if command_status == display_time  */
      os_send_signal (COMMAND);       /* signal to 'command': time changed  */
    }
    os_wait (K_TMO, 100, 0);          /* wait for 1 second                  */
  }
}


struct time rtime;                    /* temporary storage for entry time   */

/****************************************************************************/
/*        readtime: convert line input to time values & store in rtime      */
/****************************************************************************/
bit readtime (char idata *buffer)  {
  unsigned char args;                          /* number of arguments       */

  rtime.sec = 0;                               /* preset second             */
  args = sscanf (buffer, "%bd:%bd:%bd",        /* scan input line for       */
                 &rtime.hour,                  /* hour, minute and second   */
                 &rtime.min,
                 &rtime.sec);
  
  if (rtime.hour > 23  ||  rtime.min > 59  ||  /* check for valid inputs    */
      rtime.sec > 59   ||  args < 2        ||  args == EOF)  {
    printf ("\n*** ERROR: INVALID TIME FORMAT\n");
    return (0);
  }
  return (1);
}



#define ESC  0x1B                     /* ESCAPE character code              */

bit   escape;                         /* flag: mark ESC character entered   */

/****************************************************************************/
/*        Task 6 'get_escape': check if ESC (escape character) was entered  */
/****************************************************************************/
void get_escape (void) _task_ GET_ESC  {
  while (1)  {                                 /* endless loop              */
    if (_getkey () == ESC)  escape = 1;        /* set flag if ESC entered   */
    if (escape)  {                             /* if esc flag send signal   */
      os_send_signal (COMMAND);                /* to task 'command'         */
    }
  }
}


/****************************************************************************/
/*        Task 1 'command': command processor                               */
/****************************************************************************/
void command (void) _task_ COMMAND  {
  unsigned char i;

  printf (menu);                               /* display command menu      */
  while (1)  {                                 /* endless loop              */
    printf ("\nCommand: ");                    /* display prompt            */
    getline (&inline, sizeof (inline));        /* get command line input    */

    for (i = 0; inline[i] != 0; i++)  {        /* convert to uppercase      */
      inline[i] = toupper(inline[i]);
    }

    for (i = 0; inline[i] == ' '; i++);        /* skip blanks               */

    switch (inline[i])  {                      /* proceed to cmd function   */
      case 'D':                                /* Display Time Command      */
        printf ("Start Time: %02bd:%02bd:%02bd    "
                "End Time: %02bd:%02bd:%02bd\n",
                 start.hour, start.min, start.sec,
                 end.hour,   end.min,   end.sec);
        printf ("                        type ESC to abort\r");

        os_create_task (GET_ESC);              /* ESC check in display loop */
        escape = 0;                            /* clear escape flag         */
        display_time = 1;                      /* set display time flag     */
        os_clear_signal (COMMAND);             /* clear pending signals     */

        while (!escape)  {                     /* while no ESC entered      */
          printf ("Clock Time: %02bd:%02bd:%02bd\r",      /* display time   */
                   ctime.hour, ctime.min, ctime.sec);
          os_wait (K_SIG, 0, 0);               /* wait for time change/ESC  */
        }
                                               
        os_delete_task (GET_ESC);              /* ESC check not longer needd*/
        display_time = 0;                      /* clear display time flag   */
        printf ("\n\n");
        break;

      case 'T':                                /* Set Time Command          */
        if (readtime (&inline[i+1]))  {        /* read time input and       */
          ctime.hour = rtime.hour;             /* store in 'ctime'          */
          ctime.min  = rtime.min;
          ctime.sec  = rtime.sec;
        }
        break;

      case 'E':                                /* Set End Time Command      */
        if (readtime (&inline[i+1]))  {        /* read time input and       */
          end.hour = rtime.hour;               /* store in 'end'            */
          end.min  = rtime.min;
          end.sec  = rtime.sec;
        }
        break;

      case 'S':                                /* Set Start Time Command    */
        if (readtime (&inline[i+1]))  {        /* read time input and       */
          start.hour = rtime.hour;             /* store in 'start'          */
          start.min  = rtime.min;
          start.sec  = rtime.sec;
        }
        break;

      default:                                 /* Error Handling            */
        printf (menu);                         /* display command menu      */
        break;
    }   
  }
}


/****************************************************************************/
/*        signalon: check if clock time is between start and end            */
/****************************************************************************/
bit signalon ()   {
  if (memcmp (&start, &end, sizeof (struct time)) < 0)  {
    if (memcmp (&start, &ctime, sizeof (struct time)) < 0  &&
        memcmp (&ctime, &end,   sizeof (struct time)) < 0)  return (1);
  }
                                                
  else  { 
    if (memcmp (&end,   &ctime, sizeof (start)) > 0  &&
        memcmp (&ctime, &start, sizeof (start)) > 0)  return (1);
  }
  return (0);                                  /* signal off, blinking on   */
}


/****************************************************************************/
/*        Task 3 'blinking': runs if cur. time is outside start & end time  */
/****************************************************************************/
void blinking (void) _task_ BLINKING  {        /* blink yellow light        */
  red___1 = 0;                                 /* all lights off            */
  yellow1 = 0;
  green_1 = 0;
  stop__1 = 0;
  walk__1 = 0;
  red___2 = 0;
  yellow2 = 0;
  green_2 = 0;
  stop__2 = 0;
  walk__2 = 0;

  while (1)  {                                 /* endless loop              */
    phaseno = 10;
    yellow1 = 1;                               /* yellow light on           */
    yellow2 = 1;                               
    os_wait (K_TMO, 30, 0);                    /* wait for timeout: 30 ticks*/
    yellow1 = 0;                               /* yellow light off          */
    yellow2 = 0;
    os_wait (K_TMO, 30, 0);                    /* wait for timeout: 30 ticks*/
    if (signalon ())  {                        /* if blinking time over     */
      os_create_task (LIGHTS);                 /* start lights              */
      os_delete_task (BLINKING);               /* and stop blinking         */
    }
  }
}


/****************************************************************************/
/*      Task 4 'lights': executes if cur. time is between start & end time  */
/****************************************************************************/
void lights (void) _task_ LIGHTS  {            /* traffic light operation   */
  /* *** P H A S E  9 ***             */
  /* dir 1: all red                   */
  /* dir 2: all red                   */
  phaseno = 9;
  red___1 = 1;                                 /* red & stop lights on      */
  yellow1 = 0;
  green_1 = 0;
  stop__1 = 1;
  walk__1 = 0;
  red___2 = 1;                                 
  yellow2 = 0;
  green_2 = 0;
  stop__2 = 1;
  walk__2 = 0;
  while (1)  {                                 /* endless loop              */

    if (!signalon ())  {                       /* if signal time over       */
      os_create_task (BLINKING);               /* start blinking            */
      os_delete_task (LIGHTS);                 /* stop lights               */
    }

    /* *** P H A S E  0 ***             */
    /* dir 1: prepare for green         */
    /* dir 2: stick to red              */
    phaseno = 0;
    red___1 = 1;
    yellow1 = 1;
    yellow2 = 0;
    red___2 = 1;
    os_clear_signal (LIGHTS);
    keypressed1 = 0;
    keypressed2 = 0;
    cardetected1 = 0;
    cardetected2 = 0;
    os_wait (K_TMO, 30, 0);                    /* wait for timeout: 30 ticks*/

    /* *** P H A S E  1 ***             */
    /* dir 1: switch to green           */
    /* dir 2: stick to red, allow walk  */
    phaseno = 1;
    red___1 = 0;
    yellow1 = 0;
    green_1 = 1;
    stop__2 = 0;                               
    walk__2 = 1;
    os_wait (K_TMO, 30, 0);                    /* wait for timeout: 30 ticks*/

    /* *** P H A S E  2 ***             */
    /* dir 1: accept pedestrian button  */
    /* dir 2: accept car detect         */
    phaseno = 2;
    os_wait (K_TMO + K_SIG, 250, 0);           /* wait for timeout & signal */

    /* *** P H A S E  3 ***             */
    /* dir 1: switch to yellow          */
    /* dir 2: stick to red, forbid walk */
    phaseno = 3;
    green_1 = 0;
    yellow1 = 1;
    stop__2 = 1;                               
    walk__2 = 0;
    os_wait (K_TMO, 30, 0);                    /* wait for timeout: 30 ticks*/


    /* *** P H A S E  4 ***             */
    /* dir 1: switch to red             */
    /* dir 2: prepare for green         */
    phaseno = 4;
    red___1 = 1;
    yellow1 = 0;
    yellow2 = 1;
    os_clear_signal (LIGHTS);
    keypressed1 = 0;
    keypressed2 = 0;
    cardetected1 = 0;
    cardetected2 = 0;
    os_wait (K_TMO, 30, 0);                    /* wait for timeout: 30 ticks*/

    /* *** P H A S E  5 ***             */
    /* dir 1: stick to red, allow walk  */
    /* dir 2: switch to green           */
    phaseno = 5;
    stop__1 = 0;                               
    walk__1 = 1;
    red___2 = 0;
    yellow2 = 0;
    green_2 = 1;
    os_wait (K_TMO, 30, 0);                    /* wait for timeout: 30 ticks*/

    /* *** P H A S E  6 ***             */
    /* dir 1: accept car detect         */
    /* dir 2: accept pedestrian button  */
    phaseno = 6;
    os_wait (K_TMO + K_SIG, 250, 0);           /* wait for timeout & signal */

    /* *** P H A S E  7 ***             */
    /* dir 1: stick to red, forbid walk */
    /* dir 2: switch to yellow          */
    phaseno = 7;
    stop__1 = 1;                               
    walk__1 = 0;
    green_2 = 0;
    yellow2 = 1;
    os_wait (K_TMO, 30, 0);                    /* wait for timeout: 30 ticks*/
  }
}


/****************************************************************************/
/*        Task 5 'keyread': process key strokes from pedest. push buttons   */
/****************************************************************************/
void keyread (void) _task_ KEYREAD  {
  while (1)  {                                 /* endless loop              */
    if (phaseno < 4) { /* phaseno = 0..3 */
      if (!key2) {                             /* if key pressed            */
        keypressed2 = 1;
        os_send_signal (LIGHTS);               /* send signal to 'lights'   */
        os_wait (K_TMO, 5, 0);                 /* wait for timeout: 5 ticks */
      }
    }
    else {  /* phaseno = 4..7 */
      if (!key1) {                             /* if key pressed            */
        keypressed1 = 1;
        os_send_signal (LIGHTS);               /* send signal to 'lights'   */
        os_wait (K_TMO, 5, 0);                 /* wait for timeout: 5 ticks */
      }
    }
    os_wait (K_TMO, 2, 0);                     /* wait for timeout: 2 ticks */
  }
}


/****************************************************************************/
/*        Task 6 'car_det1': process interrupt from car detector 1          */  
/****************************************************************************/
void car_det1 (void) _task_ CAR_DET1 {
  os_attach_interrupt (0);                     /* Attach INT0               */  
  TCON |= 0x01;                                /* Use edge-triggered        */
  while (1)  {                                 /* endless loop              */
    os_wait (K_INT, 0xff, 0);                  /* Wait for interrupt        */  
    if (phaseno > 3) { /* phaseno = 4..7 */
      if (!cardetected2 && !keypressed1 && !cardetected1) {
        os_send_signal (LIGHTS);               /* send signal to 'lights'   */
      }
    }
    cardetected1 = 1;
  }
}


/****************************************************************************/
/*        Task 7 'car_det2': process interrupt from car detector 2          */
/****************************************************************************/
void car_det2 (void) _task_ CAR_DET2 {
  os_attach_interrupt (2);                     /* Attach INT1               */  
  TCON |= 0x04;                                /* Use edge-triggered        */
  while (1)  {                                 /* endless loop              */
    os_wait (K_INT, 0xff, 0);                  /* Wait for interrupt        */  
    if (phaseno < 4) { /* phaseno = 0..3 */
      if (!cardetected1 && !keypressed2 && !cardetected2) {
        os_send_signal (LIGHTS);               /* send signal to 'lights'   */
      }
    }
    cardetected2 = 1;
  }
}

/****************************************************************************/
/*        MAIN : Start the system                                           */
/****************************************************************************/
void main(void)
{
   os_start_system (INIT);                     /* start the first task      */
}
